#pragma once

#include <map>
using std::map;

template <typename ClassIDType, typename BaseClassType>
class ObjectFactory
{
private:
	// Function pointer for object creation
	typedef BaseClassType* (*ObjectCreator)(void);

	map<ClassIDType, ObjectCreator> m_ObjectMakers;

	// Instance to the singleton
	static ObjectFactory<ClassIDType, BaseClassType> sm_Instance;

	// TOE
	ObjectFactory(void) {}
	ObjectFactory(const ObjectFactory&);
	ObjectFactory& operator=(const ObjectFactory&);
	~ObjectFactory(void) {}

	template<typename DerivedClassType>
	static BaseClassType* ConstructObject(void)
	{
		return new DerivedClassType;
	}
public:

	// Get instance of singleton
	static ObjectFactory<ClassIDType, BaseClassType>* GetInstance(void)
	{
		return &sm_Instance;
	}

	template<typename DerivedClassType>
	bool RegisterClassType(ClassIDType id)
	{
		// Database definition
		std::pair<ClassIDType, ObjectCreator> objTypeDef;

		objTypeDef.first	= id;
		objTypeDef.second	= ConstructObject<DerivedClassType>;

		// Add information to database
		m_ObjectMakers.insert(objTypeDef);

		return true;
	}

	// Unregister a class type
	bool UnregisterClassType(ClassIDType id)
	{
		// Find the correct class type to unregister
		map<ClassIDType, ObjectCreator>::iterator iter = m_ObjectMakers.find(id);
	
		// Make sure the class type was found
		if(iter == m_ObjectMakers.end())
		{
			return false;
		}

		// Remove class information
		m_ObjectMakers.erase(iter);

		return true;
	}

	// Create an object of the given class
	BaseClassType* CreateObject(ClassIDType id)
	{
		map<ClassIDType, ObjectCreator>::iterator iter = m_ObjectMakers.find(id);

		// Error checking
		if(iter == m_ObjectMakers.end())
		{
			return NULL;
		}

		// Create class object
		return (*iter).second();
	}

	void ShutdownObjectFactory(void)
	{
		m_ObjectMakers.clear();
	}
};

template<typename ClassIDType, typename BaseClassType>
ObjectFactory<ClassIDType, BaseClassType>
ObjectFactory<ClassIDType, BaseClassType>::sm_Instance;